package view;

import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Locale;
import java.util.Observer;
import java.util.Observable;
 
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;

import controller.Actions;
import controller.Controller;

import model.CityModel;
import model.CountryModel;
import model.WeatherModel;

/**
 * Wetter-View-Objekt 
 * @author Bernd Taube
 * @version 1.1.1
 *
 */
public class WeatherView extends JFrame implements Observer, ViewInterface {

	/**
	 * serielle Versions-ID
	 */
	private static final long serialVersionUID = -7782520040452869688L;

	// Instanz-Variablen
	private Controller control = null;
	private Timer timer = null;
	private ArrayList<CountryModel> countryList = new ArrayList<CountryModel>();
	private ArrayList<CityModel> cityList = new ArrayList<CityModel>();

	// Komponeten die aktualisiert werden können
	private JComboBox cbCountries   = new JComboBox();
	private JComboBox cbCities      = new JComboBox();
    private JLabel labelTime        = new JLabel();
    private JLabel labelDate        = new JLabel();
	private JLabel labelObsTime     = new JLabel();
	private JLabel labelObsDate     = new JLabel();
	private JLabel labelTemparature = new JLabel();
	private JLabel labelCloud       = new JLabel();
	private JLabel labelWind        = new JLabel();
	private JLabel labelWindDir     = new JLabel();
	private JLabel labelVisibility  = new JLabel();
	private JLabel labelAirPressure = new JLabel();
	private JButton buttonActualize = new JButton("Aktualisiere");

    // Instanzen für Datum und Uhrzeit
    private Calendar cal = Calendar.getInstance();
    private DateFormat tf;
    private DateFormat df;
    private int count = 0;
    private StringBuffer actualize = new StringBuffer();

	/**
	 * Konstruktor 
	 * @param control
	 */
    public WeatherView(Controller control) {
		// Controller setzen
		setController(control);
		// Fenster aufbauen
		fenster();
        // und Los!
        build();
    }

    /**
     * Aufbau des Fensters
     */
	public void fenster() {
		// Layout
        setTitle("generelle Wetterstation");
        setLayout(new GridLayout(0, 1));
        setBounds(100, 100, 600, 400);
    	setLookAndFeel();

    	// Componenten einfügen
    	//setMenuBar(); // TODO noch nicht implementiert, da noch keine Actions
	    this.add( getDateTimePanel() );
	    this.add( getCountryCityPanel() ); 
	    this.add( getWeatherPanel() );
	    this.add( getObsDateTimePanel() );
	    this.add( getFooterPanel() );
	}
 
    /**
     * Look and feel setzen
     */
    private void setLookAndFeel() {
		try {
			UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());			
		} catch(Exception e) {
			
		}
    }

    /**
     * MenuBar setzen
     * TODO noch nicht implementiert, da noch keine Actions
     */
	private void setMenuBar() {
		JMenuBar menuBar = new JMenuBar();
		// Menüs
		JMenu menuFile  = new JMenu("Datei");
		JMenu menuExtra = new JMenu("Extras");
		JMenu menuHelp  = new JMenu("Hilfe");

		// Menü: "Datei"
		JMenuItem menuItemExit    = new JMenuItem("Beenden");
		// Menü: "Extras"
		JMenuItem menuItemOptions = new JMenuItem("Optionen");
		// Menü: "Hilfe"
		JMenuItem menuItemAbout = new JMenuItem("Über...");
		
		// MenuItems eintragen
		menuFile.add(menuItemExit);
		menuExtra.add(menuItemOptions);
		menuHelp.add(menuItemAbout);
		
		// Menüs in die MenuBar übernehmen
		menuBar.add(menuFile);
		menuBar.add(menuExtra);
		menuBar.add(menuHelp);

		// MenuBar setzen
		setJMenuBar(menuBar);
	}

	/**
	 * Gibt ein Panel mit Datum/Uhrzeit zurück
	 * @return Panel mit Datum/Uhrzeit
	 */
	private JPanel getDateTimePanel() {
	    JPanel panel = new JPanel();

	    // Layout
	    panel.setLayout(new GridLayout(0, 1));
	    labelDate.setHorizontalAlignment(JLabel.CENTER);
	    labelTime.setHorizontalAlignment(JLabel.CENTER);
	    labelTime.setFont(new Font("Arial", Font.BOLD, 24));
	    labelDate.setFont(new Font("Arial", Font.PLAIN, 12));

	    // Datums und Zeit-Format festlegen
		tf = DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.GERMANY);
		df = DateFormat.getDateInstance(DateFormat.LONG, Locale.GERMANY);

	    // Panel aufbereiten
	    panel.add(labelTime);
	    panel.add(labelDate);
	    panel.setVisible(true);

	    // Timer
	    setTimer();

	    // Panel zurückgeben
	    return panel;
	}

	/**
	 * gibt ein Panel mit dem Beobachtungsdatum/Zeit zurück
	 * @return Panel mit Beobachtungsdatum/Zeit
	 */
	private JPanel getObsDateTimePanel() {
	    JPanel panel = new JPanel();
	    JLabel labelObs = new JLabel("Wetterdaten von");

	    // Layout
	    panel.setLayout(new GridLayout(0, 3));
	    labelObs.setHorizontalAlignment(JLabel.CENTER);
	    labelObsDate.setHorizontalAlignment(JLabel.CENTER);
	    labelObsTime.setHorizontalAlignment(JLabel.CENTER);
	    labelObs.setFont(new Font("Arial", Font.PLAIN, 8));
	    labelObsTime.setFont(new Font("Arial", Font.PLAIN, 8));
	    labelObsDate.setFont(new Font("Arial", Font.PLAIN, 8));

	    // Panel aufbereiten
		panel.add(labelObs);
	    panel.add(labelObsTime);
	    panel.add(labelObsDate);
	    panel.setVisible(true);

	    // Panel zurückgeben
	    return panel;
	}
	
	private JPanel getFooterPanel() {
	    JPanel panel = new JPanel();

		// Layout
	    panel.setLayout(new FlowLayout());
	    
		// ActionListener hinzufügen
		buttonActualize.addActionListener( control.getActionListener(Actions.GET_CURRENT_WEATHER) );
		
	    // Panel aufbereiten
		panel.add(buttonActualize);
		panel.setVisible(true);

	    // Panel zurückgeben
	    return panel;
	}

	/**
	 * gibt ein Panel mit dem Land und der Stadt zurück
	 * @return Panel mit Land/Stadt
	 */
	private JPanel getCountryCityPanel() {
	    JPanel panel = new JPanel();

	    // Layout
	    panel.setLayout(new FlowLayout());
	    
 	    // Listener hinzufügen
	    control.addListener(cbCountries, Actions.GET_CITIES);
	    control.addListener(cbCities, Actions.GET_CURRENT_WEATHER);

	    // Panel aufbereiten
    	panel.add(cbCountries);
    	panel.add(cbCities);
	    panel.setVisible(true);
		
	    // Panel zurückgeben
	    return panel;
	}

	/**
	 * Gibt ein Panel mit den Wetterdaten zurück
	 * @return Panel mit den Wetterdaten
	 */
	private JPanel getWeatherPanel() {
	    JPanel panel = new JPanel();
	    panel.setLayout(new GridLayout(0, 3));
	    // Label hinzufügen
	    panel.add(labelTemparature);
		panel.add(labelCloud);
		panel.add(labelWind);
		panel.add(labelWindDir);
		panel.add(labelVisibility);
		panel.add(labelAirPressure);
	    // Panel zurückgeben
	    return panel;
	}

	/**
	 * Timer setzen und starten
	 */
	private void setTimer() {
		timerUpdate(); // nur damit die "Verzögerung" nicht bemerkt werden kann
		timer = new Timer(1000, new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent arg0) {
				timerUpdate();
			}
		});
		timer.start();
	}

	/**
	 * Timer-Update: Update vom Timer
	 */
	private void timerUpdate() {
		cal.setTimeInMillis(System.currentTimeMillis());
	    labelTime.setText(tf.format(cal.getTime()));
	    labelDate.setText(df.format(cal.getTime()));
	    // TODO Aktualisierung alle x Sekunden / Minuten z.B.: alle 10 Minuten
	    // Aktualisierungs-Button
		count = 60 - cal.get(Calendar.SECOND);
	    actualize.setLength(0);
	    actualize.append("Aktualisiere ");
	    actualize.append(count);
	    buttonActualize.setText(actualize.toString());
	    // jede volle Minute: Auto-Update
		if(count == 60) {
			buttonActualize.doClick();
		}
	}
	
	/**
	 * Build: Default-Verhalten festlegen
	 */
	@Override
	public void build() {
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setVisible(true);
	}

	/**
	 * Controller setzen
	 */
	@Override
	public void setController(Controller control) {
		this.control = control;
	}

	/**
	 * Update-Funktion aus dem Observer
	 */
	@Override
	public void update(Observable arg0, Object arg1) {
		// Update vom WeatherModel
		if(arg1 instanceof WeatherModel) {
        	update((WeatherModel) arg1);
        	return;
        }
		// Update vom CountryModel oder CityModel
		if( (arg1 instanceof ArrayList<?>) && ((ArrayList<?>) arg1).size() > 0) {
			update((ArrayList<?>) arg1);
			return;
		}
	}

	/**
	 * Update-Funktion aus der CountryList oder CityList
	 * @param list CountryList oder CityList
	 */
	public void update(ArrayList<?> list) {
		// Update vom CityModel
		if(list.get(0) instanceof CityModel) {
			cbCities.setEnabled(false);
			cbCities.removeAllItems();
			cityList = (ArrayList<CityModel>)list;
			for(int i = 0; i < cityList.size(); i++) {
				cbCities.addItem( cityList.get(i) );
			}
			cbCities.setEnabled(true);
			return;
		}
		// Update vom CountryModel
		if(list.get(0) instanceof CountryModel) {
			cbCountries.setEnabled(false);
			cbCountries.removeAllItems();
			countryList = (ArrayList<CountryModel>)list;
			for(int i = 0; i < countryList.size(); i++) {
				cbCountries.addItem( countryList.get(i) );
			}
			cbCountries.setEnabled(true);
			return;
		}
	}
	
	/**
	 * Update-Funktion vom WeatherModel
	 * @param model
	 */
	public void update(WeatherModel model) {
		labelTemparature.setText( Double.toString(model.getTemperature()) + " °C" );
		labelCloud.setText(model.getCloudiness());
		labelWind.setText( Double.toString(model.getWind().getSpeedKnot()) + " Knoten" );
		labelWindDir.setText(model.getWindDirectionInfo());
		labelVisibility.setText( Double.toString(model.getVisibility()) + " Meter" );
		labelAirPressure.setText( Double.toString(model.getAirPressure()) +" hPa" );
		// TODO Formatierung
		// Datum und Uhrzeit
		labelObsDate.setText(model.getDate());
		labelObsTime.setText(model.getObservationTime());
	}

}